
#ifndef NOSAL_EXEC_INTERNAL_EXECUTION_MANAGER_H_
#define NOSAL_EXEC_INTERNAL_EXECUTION_MANAGER_H_

#include <memory>
#include <set>
#include <string>
#include <vector>

#include "nosal/core/result.h"
#include "nosal/log/logger.h"

#include "config/execution_manifest.h"
#include "config/machine_manifest.h"
#include "executables_handler.h"
#include "function_group.h"
#include "processed_set_state_request.h"
#include "state_change_handler.h"

//##include "nosal/exec/internal/state_client_impl.h"
//##include "nosal/exec/internal/findprocess/em_find_process_wrapper.h"
//##include "nosal/exec/internal/dependency_calculator.h"

namespace netaos {
namespace nosal {
namespace exec {
namespace internal {

/// @brief Class to manage the Execution Management.
class ExecutionManager final
{
public:
    /// @brief Run executes a machine configuration specified by rootPath
    ///
    /// @param rootPath path to a dir with machine configuration
    ///
    /// @returns If the execution terminates successfully then the result
    ///         is EXIT_SUCCESS otherwise the result is EXIT_FAILURE
    ///
    /// @note The function may not return if shutdown is executed by another process
    static int Run(const char* rootPath) noexcept;

    /// @brief ProcessExecutionStateReports receives reports from processes
    void ProcessExecutionStateReports();

private:
    /// @brief The PlatformStates enum represents possible platform execution states
    enum class PlatformStates
    {
        kRunning,
        kRestarting,
        kShuttingdown,
        kUnrecoverableState
    };

    ExecutionManager() = delete;
    ExecutionManager(const ExecutionManager&) = delete;
    ExecutionManager& operator=(const ExecutionManager&) = delete;

    /// @brief Constructor of Execution Manager instance.
    ///
    /// @param rootPath Path to a base directory for where to search configuration files and Adaptive Applications
    ///
    explicit ExecutionManager(const char* rootPath);

#if 0
    /// @brief FindStartupConfiguration
    ///
    /// @param pid Posix process id
    ///
    /// @returns Pointer to the found startup configuration or nullptr
    std::shared_ptr<StartupConfiguration> FindStartupConfiguration(pid_t pid) const;
#endif
    /// @brief Initialize Load the Machine manifest and the Execution manifests
    void Initialize();

    /// @brief LoadMachineConfiguration loads Machine Manifest
    ///
    netaos::nosal::core::Result<void> LoadMachineConfiguration();

    /// @brief InitializeCgroups initializes all cgroups configured in the Machine Manifest
    ///
    // void InitializeCgroups();

    /// @brief PrepareFileSystem prepares the File System to be able to create and access cgroups
    ///
    //bool PrepareFileSystem();

    /// @brief Load the Execution Manifests.
    /// @returns Empty Result on success, error if no Executables were found.
    netaos::nosal::core::Result<void> LoadExecutionManifests();

    /// @brief Reparse Execution Manifests.
    //netaos::nosal::core::Result<void> ReparseManifests();

    /// @brief Initialize the process lookup for IAM
    //void InitializeFindProcess();

    /// @brief Sends an error message to the StateClient if StateClient belongs to non-StateManagement app.
    /// @param requestMessage The received message.
    /// @param requestor The Process issuing the request.
    //void RejectStateClientRequest(const PipeMessage& requestMessage, std::shared_ptr<StartupConfiguration> requestor);

    /// @brief Processes all current state transitions a bit.
    void ProcessStateTransitions();

    /// @brief Checks whether a FunctionGroup is already in a requested state.
    /// @param[in] stateChange The struct with the requested state change.
    /// @returns True if FunctionGroup is already in requested state, false otherwise.
    //bool CheckIfAlreadyInState(const StateClientImpl::StateChange& stateChange);

    /// @brief Processes new requests coming on the system state pipe
    ///
    /// Gets messages from MachineStatePipe and processes them accordingly
    ///
    void ProcessStateClientRequests();

    /// @brief Processes SetState request and sends response to StateClient
    ///
    /// @param request [in] - a request to be processed
    /// @param startupConfiguration [in,out] - needed for sending response
    ///
    bool ProcessSetStateRequest(ProcessedSetStateRequest& processedRequest);
#if 0
    /// @brief Processes GetExecutionError request and sends response to StateClient
    ///
    /// @param request [in] - a request to be processed
    /// @param startupConfiguration [in,out] - needed for sending response
    ///
    void ProcessGetExecutionErrorRequest(const StateClientImpl::GetExecutionErrorRequest& request,
        std::shared_ptr<StartupConfiguration> startupConfiguration);

    /// @brief Sends the response of a GetExecutionErrorRequest to StateClient.
    ///
    /// @param startupConfiguration [in, out] - needed for sending the response.
    /// @param response [in] - the response itself.
    ///
    void SendGetExecutionErrorResponse(std::shared_ptr<StartupConfiguration> startupConfiguration,
        const StateClientImpl::GetExecutionErrorResponse& response);

    /// @brief Sends the response of a SetState to StateClient.
    ///
    /// @param startupConfiguration [in, out] - needed for sending the response.
    /// @param response [in] - the response itself.
    /// @param responseTimeout [in] - the timeout after which no response shall be sent.
    ///
    void SendSetStateResponse(std::shared_ptr<StartupConfiguration> startupConfiguration,
        const StateClientImpl::SetStateResponse& response,
        std::chrono::time_point<std::chrono::steady_clock> responseTimeout);

    /// @brief Sends the response to a GetInitialMachineStateTransition request to StateClient.
    /// @param requestor[in, out] The Process issuing the request.
    /// @param response[in] The response itself.
    void SendGetInitialMachineStateTransitionResponse(std::shared_ptr<StartupConfiguration> startupConfiguration,
        const StateClientImpl::GetInitialMachineStateTransitionResponse& response);

    /// @brief Sends the response of an unknown StateClient request.
    /// @param[in, out] startupConfiguration The Process issuing the request.
    void SendUnknownResponse(std::shared_ptr<StartupConfiguration> startupConfiguration);

#endif
    /// @brief Refreshes state of each FG in case of unexpected termination of a process
    void UpdateFunctionGroupsStates();

    /// @brief Notify StateClient about transition of a FunctionGroup State to Undefined State
    ///
    /// @param functionGroup [in] - a FunctionGroup that switched to Undefined State
    ///
    void NotifyUndefinedState(const FunctionGroup& functionGroup);

    /// @brief Destructor of Execution Manager instance.
    ~ExecutionManager();

    /// @brief Tries to terminate all processes and eliminate them from the list of executed processes.
    /// @param[in] kill If true, processes are killed, else terminated by SIGTERM.
    void TerminateAllProcesses(bool kill);

    /// @brief Enter the unrecoverable state.
    /// Shall happen only on dramatic failures (e. g. failed Startup state) that EM cannot escape on its own.
    void EnterUnrecoverableState();

    /// @brief Run the Application Execution Manager
    ///
    /// Use "Run" to start the initialization sequence of the Application Execution Manager
    /// - leads to an endless running loop until a shutdown is initiated
    void Run();
#if 0
    /// @brief Prepares directory for IPC files for communication with StateClient. Creates the directory if it does not
    /// exist, otherwise it removes content of the directory
    static void PrepareStateClientIpcDir();
#endif
    const std::string root_path_;
    PlatformStates platform_state_;
    std::unique_ptr<config::MachineManifest> manifest_;
    std::map<std::string, FunctionGroup> function_groups_;
    std::set<std::shared_ptr<StartupConfiguration>> executed_startup_configurations_;
    std::vector<std::string> machine_specific_environment_variables_;

    std::unique_ptr<ExecutablesHandler> executables_handler_;

#if 0
    std::unique_ptr<netaos::nosal::exec::internal::findprocess::EmFindProcessWrapper> findProcess_;

    /// @brief Paths of local sockets for notifying StateClients about undefined FG state
    std::vector<std::string> notifierSocketsPaths_;
#endif
    /// @brief Indicates whether the initial switch to MachineFG state Startup was successful.
    bool was_initial_machine_state_transition_successful_{false};

    /// @brief Indicates if there are ongoing state transitions handled by ExecutionManager.
    bool ongoing_state_transition_{false};

    /// @brief A collection of all current set state requests for ExecutionManager.
    std::vector<ProcessedSetStateRequest> processed_requests_;

    /// @brief Manages the state changes of FunctionGroups and their StartupConfigurations.
    StateChangeHandler state_change_handler_;
};

}  // namespace internal
}  // namespace exec
}  // namespace nosal
}  // namespace netaos

#endif  // NOSAL_EXEC_INTERNAL_EXECUTION_MANAGER_H_
